package com.uzhie.jt808.service;

import com.uzhie.jt808.JT808Constant;
import com.uzhie.jt808.JT808SessionManager;
import com.uzhie.jt808.JT808SysCache;
import com.uzhie.jt808.msg.JT808MsgBean;
import com.uzhie.jt808.msg.JT808MsgHeaderBean;
import com.uzhie.jt808.task.TaskCommonWait;
import com.uzhie.jt808.util.ByteUtil;
import com.uzhie.util.CacheManager;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

/**
 * Created by TangFei on 2018/7/23.
 */
public class JT808ServiceImpl implements JT808Service {

    private Logger logger = Logger.getLogger(this.getClass());
    @Autowired
    @Qualifier(value = "sessionManager")
    private JT808SessionManager sessionManager;

    @Autowired
    @Qualifier(value = "jt808ThreadExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private CacheManager cacheManager;

    private JT808SysCache sysCache;

    private Map<Integer, Class<? extends Callable>> taskMap;

    {
        taskMap = new HashMap<>();
        taskMap.put(JT808Constant.MSG_ID.PLA_REQ_MONITOR, TaskCommonWait.class);
        taskMap.put(JT808Constant.MSG_ID.PLA_REQ_MONITOR_CONTROL, TaskCommonWait.class);
    }

    @Override
    public boolean needReturn(int msgID) {
        return taskMap.containsKey(msgID);
    }

    public void setSysCache(JT808SysCache sysCache) {
        this.sysCache = sysCache;
    }

    /**
     * 下发指令到终端
     *
     * @param msgID  消息ID
     * @param simNum 终端SIM卡号
     * @param body   消息体
     * @return
     */
    @Override
    public boolean post(int msgID, String simNum, byte[] body) {
        return post(msgID, sysCache.getPlaFlowNum(), simNum, body);
    }

    public boolean postForMedia(int msgID, String simNum, byte[] body) {
        return post(msgID, sysCache.getPlaFlowNum(), simNum, body);
    }


    @Override
    public boolean post(int msgID, List<String> simNums, byte[] body) {
        for (String simNum : simNums) {
            post(msgID, simNum, body);
        }
        return false;
    }

    private boolean post(int msgID, int flowNum, String simNum, byte[] body) {
        boolean result = false;
        try {
            JT808MsgBean msgBean = new JT808MsgBean();
            JT808MsgHeaderBean headerBean = new JT808MsgHeaderBean();
            headerBean.setMsgID(msgID);
            headerBean.setFlowNum(flowNum);
            headerBean.setSimNum(simNum);
            msgBean.setHeader(headerBean);
            msgBean.setBodyBytes(null == body ? new byte[]{} : body);
            Channel channel = sessionManager.getSession(simNum).getChannel();
            channel.writeAndFlush(msgBean);
            if (msgID == JT808Constant.MSG_ID.PLA_REQ_UP_FILE) {
                sysCache.putCacheVal(flowNum, body);
            }
            result = true;
            logger.info("通道 " + channel + " 发送 808 指令 " + msgBean.getHeader().getHexMsgID() + " 结果为 " + result);
        } catch (Exception e) {
            logger.error("消息体转换异常！发送 808 指令 " + ByteBufUtil.hexDump(ByteUtil.integerTo2Bytes(msgID)) + " 结果为 " + result, e);
        } finally {
            return result;
        }
    }

    @Override
    public boolean post(String jmsID, int msgID, String simNum, byte[] body) {
        int flowNum = sysCache.getPlaFlowNum();
        boolean b = post(msgID, flowNum, simNum, body);
        if (b) {
            sysCache.putCacheVal(flowNum, jmsID);
        }
        return b;
    }

    @Override
    public void onlineDev() {
    }

    @Override
    public boolean isOnline(String simNum) {
        boolean result = false;
        try {
            Channel channel = sessionManager.getSession(simNum).getChannel();
            if (null != channel) {
                result = channel.isRegistered();
            } else {
                result = false;
            }
        } catch (NullPointerException e) {
            result = false;
        } finally {
            return result;
        }
    }

    @Override
    public <T> T postWithResult(int msgID, String simNum, byte[] body, Class<T> resultClass) {
        T result = null;
        try {
            JT808MsgBean msgBean = new JT808MsgBean();
            JT808MsgHeaderBean headerBean = new JT808MsgHeaderBean();
            headerBean.setMsgID(msgID);
            headerBean.setFlowNum(sysCache.getPlaFlowNum());
            headerBean.setSimNum(simNum);
            msgBean.setHeader(headerBean);
            msgBean.setBodyBytes(null == body ? new byte[]{} : body);
            Channel channel = sessionManager.getSession(simNum).getChannel();
            channel.writeAndFlush(msgBean);
            Class<? extends Callable> taskClass = taskMap.get(msgID);
            Constructor<? extends Callable> constructor = taskClass.getDeclaredConstructor(String.class, CacheManager.class, JT808SysCache.class);
            StringBuilder lockBuild = new StringBuilder("POST_");
            lockBuild.append(simNum).append("_").append(msgID).append("_").append(headerBean.getFlowNum());
            Callable instance = constructor.newInstance(lockBuild.toString(), cacheManager, sysCache);
            java.util.concurrent.Future<T> future = taskExecutor.submit(instance);
            result = future.get();
        } catch (ExecutionException | InterruptedException | InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            logger.error(e);
            e.printStackTrace();
        } finally {
            return result;
        }
    }
}
